package cn.xydata.es;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.*;
import co.elastic.clients.elasticsearch._types.aggregations.*;
import co.elastic.clients.elasticsearch._types.query_dsl.*;
import co.elastic.clients.elasticsearch.core.UpdateByQueryResponse;
import co.elastic.clients.elasticsearch.core.search.SourceConfig;
import co.elastic.clients.json.JsonData;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * es工具类
 *
 * @author tz
 * @create 2024-02-27
 */
public class EsUtil {

    /**
     * 排序选项
     *
     * @param field 字段
     * @param asc   是否正序
     * @return {@link SortOptions}
     */
    public static SortOptions sortOptions(String field, boolean asc) {
        SortOrder sortOrder;
        if (asc) {
            sortOrder = SortOrder.Asc;
        } else {
            sortOrder = SortOrder.Desc;
        }
        FieldSort fieldSort = new FieldSort.Builder().field(field).order(sortOrder).build();
        return new SortOptions.Builder().field(fieldSort).build();
    }

    /**
     * 通配符查询
     * 与SQL中的 LIKE 类似
     *
     * @param field 字段
     * @param value 值 左右两边需要加上'*'号,类似SQL中的'%'
     * @return {@link Query}
     */
    public static Query wildcardQuery(String field, String value) {
        WildcardQuery wildcardQuery = new WildcardQuery.Builder().field(field).value(value).build();
        return new Query.Builder().wildcard(wildcardQuery).build();
    }

    /**
     * match查询 逐个匹配(非严格) value和 field实际值有交集即可匹配
     * value的分割方式要和映射的分割方式保持一致
     *
     * @param field 字段
     * @param value 以分割符拼接的value
     * @return {@link Query}
     */
    public static Query matchQuery(String field, String value) {
        MatchQuery matchQuery = new MatchQuery.Builder().field(field).query(value).build();
        return new Query.Builder().match(matchQuery).build();
    }

    /**
     * 某个字段的列表查询
     * 列表中任何一个匹配就会匹配成功 列表中的单一value值与SQL中 IN 类似
     * 其中should与SQL中的 OR 类似
     *
     * @param field 字段
     * @param valueList 值列表 这里会自动加上'*'进行一个大范围
     * @return
     */
    public static Query shouldWildcardQuery(String field, List<String> valueList) {
        List<Query> queryList = new ArrayList<>();
        for (String value : valueList) {
            queryList.add(EsUtil.wildcardQuery(field, "*" + value + "*"));
        }
        BoolQuery financeIndustryBoolQuery = new BoolQuery.Builder().should(queryList).build();
        return new Query.Builder().bool(financeIndustryBoolQuery).build();
    }

    /**
     * 精确查询
     * 与SQL中的 = 类似
     *
     * @param field 字段
     * @param value 值
     * @return {@link Query}
     */
    public static Query termQuery(String field, String value) {
        TermQuery termQuery = new TermQuery.Builder().field(field).value(value).build();
        return new Query.Builder().term(termQuery).build();
    }

    /**
     * 范围查询
     * 与SQL中的 BETWEEN 类似
     *
     * @param field       字段
     * @param from        开始值
     * @param includeFrom 是否包含开始
     * @param to          结束值
     * @param includeTo   是否包含结束
     * @return {@link Query}
     */
    public static Query rangeQuery(String field, Object from, boolean includeFrom, Object to, boolean includeTo) {
        RangeQuery.Builder builder = new RangeQuery.Builder().field(field);
        if (includeFrom) {
            builder.gte(JsonData.of(from));
        } else {
            builder.gt(JsonData.of(from));
        }
        if (includeTo) {
            builder.lte(JsonData.of(to));
        } else {
            builder.lt(JsonData.of(to));
        }
        RangeQuery rangeQuery = builder.build();
        return new Query.Builder().range(rangeQuery).build();
    }

    /**
     * 批量条件查询
     *
     * @param mustQueryList 必须匹配条件
     * @return 查询
     */
    public static Query boolQuery(List<Query> mustQueryList) {
        BoolQuery boolQuery = new BoolQuery.Builder().must(mustQueryList).build();
        return new Query.Builder().bool(boolQuery).build();
    }

    public static Aggregation sumAggregation(String sumField) {
        SumAggregation sumAggregation = new SumAggregation.Builder().field(sumField).build();
        return new Aggregation.Builder().sum(sumAggregation).build();
    }

    public static Aggregation termsSumAggregation(String termField, String termAfterField, String sumField) {
        TermsAggregation termsAggregation = new TermsAggregation.Builder().field(termField).build();
        return new Aggregation.Builder().terms(termsAggregation).aggregations(termAfterField, sumAggregation(sumField)).build();
    }

    public static Aggregation termsAggregation(String termField) {
        TermsAggregation termsAggregation = new TermsAggregation.Builder().field(termField).build();
        return new Aggregation.Builder().terms(termsAggregation).build();
    }

    public static Aggregation termsMapAggregation(String termField, Map<String, Aggregation> sumMap) {
        TermsAggregation termsAggregation = new TermsAggregation.Builder().field(termField).build();

        return new Aggregation.Builder().terms(termsAggregation).aggregations(sumMap).build();
    }

    /**
     * 寻找唯一值的总量
     * 类似于 sql中 count（*） group by cardinalityField
     *
     * @param cardinalityField 唯一值字段
     * @return 总量
     */
    public static Aggregation cardinalityAggregation(String cardinalityField) {
        CardinalityAggregation cardinalityAggregation = new CardinalityAggregation.Builder().field(cardinalityField).build();
        return new Aggregation.Builder().cardinality(cardinalityAggregation).build();
    }

    public static Script sumInlineScript(List<String> docList) {
        StringBuilder sourceScript = new StringBuilder();
        for (int i = 0; i < docList.size(); i++) {
            if (i != 0) {
                sourceScript.append(" + ");
            }
            sourceScript.append("doc['").append(docList.get(i)).append("'].value");
        }
        return script(sourceScript.toString());
    }

    /**
     * 返回Script脚本
     *
     * @param sourceScript 脚本语言
     * @return Script脚本
     */
    public static Script script(String sourceScript) {
        InlineScript inlineScript = new InlineScript.Builder().source(sourceScript).build();
        return new Script.Builder().inline(inlineScript).build();
    }

    /**
     * topHits聚合 显示聚合后的文档信息
     *
     * @param size 显示文档数量
     * @return topHits聚合
     */
    public static Aggregation topHitsAggregation(Integer size) {
        SourceConfig sourceConfig = new SourceConfig.Builder().fetch(true).build();
        TopHitsAggregation topHitsAggregation = new TopHitsAggregation.Builder().size(size).source(sourceConfig).build();
        return new Aggregation.Builder().topHits(topHitsAggregation).build();
    }

    /**
     * 一个字段后的最大值 搭配term聚合
     *
     * @param field 字段
     * @return 最大值
     */
    public static Aggregation maxAggregation(String field) {
        MaxAggregation maxAggregation = new MaxAggregation.Builder().field(field).build();
        return new Aggregation.Builder().max(maxAggregation).build();
    }

    /**
     * 一个字段后的最小值 搭配term聚合
     *
     * @param field 字段
     * @return 最小值
     */
    public static Aggregation minAggregation(String field) {
        MinAggregation minAggregation = new MinAggregation.Builder().field(field).build();
        return new Aggregation.Builder().min(minAggregation).build();
    }
}
